# Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
# (originally written in C++.. i think..)

namespace Vanilla.UI

import System
import System.Runtime.InteropServices

partial class BitmapCanvas (Canvas):
	def Between (x as int, a as int, b as int) as int:
		return Math.Max(Math.Min(a,x),b)

	def FastBlur (radius as int):
		return unless radius > 1
		
		pix = RawData
		w as int = Size.Width
		h as int = Size.Height
		wm = w - 1
		hm = h - 1
		wh = w * h
		div = radius + radius + 1
		
		r as (int) = array(int, wh)
		g as (int) = array(int, wh)
		b as (int) = array(int, wh)
		
		rsum as int
		gsum as int
		bsum as int
		
		x as int
		y as int
		i as int
		p as int
		yp as int
		yi as int
		yw as int
		
		vmin as (int) = array(int, Math.Max(w,h))
		
		divsum as int = (div+1)>>1
		divsum *= divsum
		dv as (int) = array(int, 256 * divsum)
		i=0
		while i < 256 * divsum:
			dv[i] = (i/divsum)
			i++
		
		yw = yi = 0
		
		stack as (int,2) = matrix(int, div, 3)
		stackpointer as int
		stackstart as int
		sir as (int)
		rbs as int
		r1 as int = radius + 1
		
		routsum as int
		goutsum as int
		boutsum as int
		rinsum as int
		ginsum as int
		binsum as int
		
		y = 0
		while y < h:
			rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0
			
			i = -radius
			while i <= radius:
				p = Marshal.ReadInt32(cast(IntPtr, cast(int,pix) + (yi + Math.Min(wm,Math.Max(i,0)))*4))
				
				sir = stack[i+radius, 0:3]
				sir[0] = (p & 0xff0000) >> 16
				sir[1] = (p & 0x00ff00) >> 8
				sir[2] = (p & 0x0000ff)
				
				rbs = r1 - Math.Abs(i)
				rsum += sir[0]*rbs
				gsum += sir[1]*rbs
				bsum += sir[2]*rbs
				
				if i > 0:
					rinsum += sir[0]
					ginsum += sir[1]
					binsum += sir[2]
				else:
					routsum += sir[0]
					goutsum += sir[1]
					boutsum += sir[2]
				i++
			
			stackpointer = radius
			
			x = 0
			while x < w:
				//print "r/g/b [${yi}] (length ${r.Length} or ${g.Length} or ${b.Length}) = dv[${rsum} or ${gsum} or ${bsum}] (length ${dv.Length})"
				r[yi] = dv[rsum]
				g[yi] = dv[gsum]
				b[yi] = dv[bsum]
				
				rsum -= routsum
				gsum -= goutsum
				bsum -= boutsum
				
				stackstart = stackpointer - radius + div
				sir = stack[stackstart % div, 0:3]
				
				routsum -= sir[0]
				goutsum -= sir[1]
				boutsum -= sir[2]
				
				if y == 0:
					vmin[x] = Math.Min(x + radius + 1, wm)
				
				p = Marshal.ReadInt32(cast(IntPtr, cast(int,pix) + (yw + vmin[x])*4))
				
				sir[0] = (p & 0xff0000) >> 16
				sir[1] = (p & 0x00ff00) >> 8
				sir[2] = (p & 0x0000ff)
				
				rinsum += sir[0]
				ginsum += sir[1]
				binsum += sir[2]
				
				rsum += rinsum
				gsum += ginsum
				bsum += binsum
				
				stackpointer = (stackpointer + 1) % div
				sir = stack[(stackpointer) % div, 0:3]
				
				routsum += sir[0]
				goutsum += sir[1]
				boutsum += sir[2]
				
				rinsum -= sir[0]
				ginsum -= sir[1]
				binsum -= sir[2]
				
				yi++
				x++
			yw += w
			y++
		x = 0
		while x < w:
			rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0
			
			yp = -radius*w
			
			i = -radius
			while i <= radius:
				yi = Math.Max(0,yp) + x
				
				sir = stack[i + radius, 0:3]
				
				sir[0] = r[yi]
				sir[1] = g[yi]
				sir[2] = b[yi]
				
				rbs = r1 - Math.Abs(i)
				
				rsum += r[yi] * rbs
				gsum += g[yi] * rbs
				bsum += b[yi] * rbs
				
				if i > 0:
					rinsum += sir[0]
					ginsum += sir[1]
					binsum += sir[2]
				else:
					routsum += sir[0]
					goutsum += sir[1]
					boutsum += sir[2]
				
				if i < hm:
					yp += w
				
				i++
			
			yi = x
			stackpointer = radius
			
			y = 0
			while y < h:
				try:
					Marshal.WriteInt32(cast(IntPtr, cast(int,pix) + (yi * 4)), 0xff000000 | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum])
				except e:
					pass //print "error while writing to memory - ${e}"
				
				rsum -= routsum
				gsum -= goutsum
				bsum -= boutsum
				
				stackstart = stackpointer - radius + div
				sir = stack[stackstart % div, 0:3]
				
				routsum -= sir[0]
				goutsum -= sir[1]
				boutsum -= sir[2]
				
				if x == 0:
					vmin[x] = Math.Min(y + r1, hm) * w
				p = x + vmin[y]
				
				sir[0] = r[p]
				sir[1] = g[p]
				sir[2] = b[p]
				
				rinsum += sir[0]
				ginsum += sir[1]
				binsum += sir[2]
				
				rsum += rinsum
				gsum += ginsum
				bsum += binsum
				
				stackpointer = (stackpointer + 1) % div
				sir = stack[stackpointer, 0:3]
				
				routsum += sir[0]
				goutsum += sir[1]
				boutsum += sir[2]
				
				rinsum -= sir[0]
				ginsum -= sir[1]
				binsum -= sir[2]
				
				yi += w
				y++
			x++
